/*
 * Package il.ac.biu.cs.grossmm.impl.activeData
 * File SqlManipulator.java
 * 
 * This is a part of presence service framework prototype. 
 * See javadoc for more information.
 *  
 * Copyright (C) 2006 Grossmann Mark
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package il.ac.biu.cs.grossmm.impl.activeData;

import il.ac.biu.cs.grossmm.api.OperationFailedException;
import il.ac.biu.cs.grossmm.api.data.*;
import il.ac.biu.cs.grossmm.api.flow.NodeInspector;
import il.ac.biu.cs.grossmm.api.flow.NodeManipulator;

import java.io.Serializable;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class SqlManipulator extends SqlInspector implements NodeManipulator {
	final Map<SqlNodeBase, SqlNodeModification> modificationMap;

	final SqlPublicationPoint<?> pp;

	final SqlRootNode rootNode;

	// PreparedStatement sqlAdd;
	// PreparedStatement sqlSelectParent;

	public SqlManipulator(SqlPublicationPoint<?> pp, SqlRootNode rootNode)
			throws SQLException {
		super();
		this.pp = pp;
		this.rootNode = rootNode;
		modificationMap = new HashMap<SqlNodeBase, SqlNodeModification>();
	}

	SqlNodeModification modification(Node node) {
		SqlNodeBase n = (SqlNodeBase) node;
		SqlNodeModification m = modificationMap.get(n);

		if (m == null) {
			m = new SqlNodeModification(n, this);
			modificationMap.put(n, m);
		}

		return m;
	}

	public <N> void copyNode(NodeInspector inspector, Node<N> src, Node<N> dst) {
		throw new RuntimeException("Not implemented");
	}

	@SuppressWarnings("unchecked")
	public <N, S> Node<S> createSubnode(Node<N> node, Nodeset<N, S> nodeSet)
			throws OperationFailedException {
		SqlNodeModification m = modification(node);

		SqlNodeModification m2;
		try {
			// create a new node and a modification for it
			m2 = m.createSubnode(nodeSet.index());

			// add modification to the map of modifications
			modificationMap.put(m2.node, m2);
		} catch (SQLException e) {
			throw new OperationFailedException("Could not create node", e);
		}

		return m2.node;
	}

	public <S> void removeSubnode(Node<S> subnode)
			throws OperationFailedException {
		SqlNodeModification m = modification(subnode);
		m.removeSubnode();
	}

	public <N, V> void setValue(Node<N> node, Property<N, V> property, V value)
			throws OperationFailedException {
		SqlNodeModification m = modification(node);

		m.setValue(property.index(), (Serializable) value);
	}

	public <N, V> void setAndCheck(Node<N> node, Property<N, V> property,
			V value) throws OperationFailedException {
		V v = getValue(node, property);
		if (v == null)
			if (value == null)
				return;

		if (v.equals(value))
			return;

		SqlNodeModification m = modification(node);

		m.setValue(property.index(), (Serializable) value);
	}

	@SuppressWarnings("unchecked")
	public <N, S> Set<Node<S>> getSubnodes(Node<N> node, Nodeset<N, S> nodeSet)
			throws OperationFailedException {

		// get modified node
		SqlNodeModification m = modificationMap.get(node);

		// if node was not modified - return the persistent version
		if (m == null)
			return super.getSubnodes(node, nodeSet);

		// parent node removed ?
		if (m.updateType == UpdateType.REMOVED)
			return null;

		int index = nodeSet.index();

		// get modified nodeset
		SqlNodesetModification s = m.nodesets[index];

		// if nodeset was not modified - return the persistent version
		if (s == null)
			return super.getSubnodes(node, nodeSet);

		return (Set) s.getMerged();
	}

	@SuppressWarnings("unchecked")
	public <N, P> P getValue(Node<N> node, Property<N, P> property)
			throws OperationFailedException {
		// get modified node
		SqlNodeModification m = modificationMap.get(node);

		// if node was not modified
		// or none of its values was modified - return the persistent version
		if (m == null || m.newValues == null)
			return super.getValue(node, property);

		int index = property.index();

		return (P) m.newValues[index];
	}

	public SqlInnerNode addNode(SqlNodeBase parent, int index)
			throws SQLException, OperationFailedException {
		SqlInnerMapping mapping = parent.mapping.getSubMapping(index);

		return mapping.create(parent);

	}

	void applyChanges() throws Exception {
		for (SqlNodeModification m : modificationMap.values()) {
			m.applyChanges();
		}

		rootNode.mapping.makeVisible(rootNode);
	}
}
